<?php 
/**
 * This file is part of DomAr.
 * 
 * DomAr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomAr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomAr.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
* @package DomAr
* @subpackage Plugin
*/
class DomArOrder extends DomArPlugin {
	
	
	protected $pos;
	
	public function __construct( $pos ) {
		$this->pos = (int)$pos;
	}
	
	public function increase() {
		$this->getProperty()->increase( $this->getParent() );
	}
	
	public function decrease() {
		$this->getProperty()->decrease( $this->getParent() );
	}
	
	public function toMin() {
		$this->getProperty()->toMin( $this->getParent() );
	}
	
	public function toMax() {
		$this->getProperty()->toMax( $this->getParent() );
	}
	
	public function swap( $child2 ) {
		$this->getProperty()->swap( $this->getParent(), $child2);
	}
	
	public function toPos( $pos ) {
		$this->getProperty()->toPos( $this->getParent(), $pos );
	}
	
	public function setPos( $pos ) {
		$this->pos = (int)$pos;
	}
	
	public function __get( $var ) {
		return $this->$var;
	}
	
	public function next() {
		$this->getProperty()->next( $this->getParent() );
	}
	
	public function prev() {
		$this->getProperty()->prev( $this->getParent() );
	}	
}


/**
* @package DomAr
* @subpackage Plugin
*/
class DomArProperty_Order extends DomArProperty_Plugin implements IDomArPropertyAlterTableListener,IDomArPropertyInsertListener,IDomArPropertyBeforeSetListener {
	
	protected $relationProperty;
	
	public function __construct( $name, $relationProperty ) {
		parent::__construct( $name );
		$this->relationProperty = $relationProperty;
	}
	
	public function init( DomArObject $object, array &$values ) {
		return (int)$values[$this->name];
	}
	
	public function load( DomArObject $object, &$propertyValue ) {
		$r = new DomArOrder( $propertyValue );
		$r->changeParent( $object, $this );
		return $r;
	}
	
	public function isLoaded( DomArObject $object, &$propertyValue ) {
		return is_object($propertyValue);
	}
	
	public function beforeSet( DomArObject $object, &$oldPropertyValue, &$newPropertyValue ) {
		
		//debug($newPropertyValue);
		//debug($oldPropertyValue);
		
		if( is_object( $newPropertyValue ) ) {
			$this->swap( $object, $newPropertyValue );
		
		} else if( $newPropertyValue === 'max' ) {
			$this->toMax( $object );
		
		} else if( $newPropertyValue === 'min' or $newPropertyValue === 0 ) {
			$this->toMin( $object );
		
		} else if( $newPropertyValue-1 === $oldPropertyValue->pos ) {
			$this->decrease( $object );
		
		} else if( $newPropertyValue+1 === $oldPropertyValue->pos ) {
			$this->increase( $object );
		
		} else if( $newPropertyValue > 0 ) {
			$this->toPos( $object, $newPropertyValue );
		
		} else {
			throw new InvalidArgumentException('Unknown argument '.$newPropertyValue.' given for order!');
		}
		
		$newPropertyValue = NULL;
	}	
	
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		return ($propertyValue === 'max' or $propertyValue === 'min' or is_numeric($propertyValue) && $propertyValue >= 0 or spl_object_hash($propertyValue) == spl_object_hash($object->{$this->name})) ? 0 : 1;
	}
	
	public function insert( DomArObject $object, &$propertyValue, DomArValueSet $set ) {
		
		// loads DomArOrder object as insert might not have loaded it if it is not used.
		$propertyValue = $object->{$this->name};
		
		if( is_object($object->{$this->relationProperty}) === false )
			return;
		
		if( ($setParentId = $object->{$this->relationProperty}->getId()) > 0 )
			$pos = DomSql::selectOneQuery('SELECT MAX(`'.$this->name.'`) as n FROM '.$object->getDbTable().' WHERE '.$this->relationProperty.' = '.$setParentId,'n')+1;

		$propertyValue->setPos( $pos );
		
		$set->set( $this->name, (int)$pos );
	}

	public function alterTable( DomArTable $table ) {
		$table->changeColumn( $this->name, 'int(11)', true );
		
		//$table->addIndex( $table->name . '_' . $this->relationProperty.'_'.$this->name, array($this->relationProperty, $this->name),'unique');
		$table->addIndex( $table->name . '_' .$this->name, array($this->name), 'index' );
	}
	
		
	public function increase( DomArObject $object ) {
		
		$id = $object->id;
		
		$parentId = $object->access($this->relationProperty);
		if( is_object($parentId ) ) 
			$parentId = $parentId->id;
		
		
		$r = DomSql::selectQuery('
							SELECT id, `'.$this->name.'` 
							FROM '.$object->getDBTable().' as t 
							WHERE `'.$this->relationProperty.'` = '.$parentId.' 
									AND `'.$this->name.'` >= (
										SELECT `'.$this->name.'` 
										FROM '.$object->getDBTable().' 
										WHERE id = '.$id.' 
										LIMIT 1)
							ORDER BY `'.$this->name.'` 
							LIMIT 2')->toArray();
						
		$up = $r[0];
		$down = $r[1];
		
		if( is_array($up) && is_array($down) ) {
			
			if( $item = call_user_func(array(get_class($object),'load'), $up['id'] ) )
				$item->{$this->name}->setPos($down[$this->name]);
			
			if( $item = call_user_func(array(get_class($object),'load'), $down['id'] ) ) 
				$item->{$this->name}->setPos($up[$this->name]);
			
			DomSql::query('UPDATE `'.$object->getTableName().'` SET `'.$this->name.'` = NULL WHERE id = '.$up['id'].''); 
			DomSql::query('UPDATE `'.$object->getTableName().'` SET `'.$this->name.'` = '.$up[$this->name].' WHERE id = '.$down['id'].''); 
			DomSql::query('UPDATE `'.$object->getTableName().'` SET `'.$this->name.'` = '.$down[$this->name].' WHERE id = '.$up['id'].''); 
		}
	}
	
	public function decrease( DomArObject $object ) {
		
		$id = $object->id;
		
		$parentId = $object->access($this->relationProperty);
		if( is_object($parentId ) ) 
			$parentId = $parentId->id;
		

		$r = DomSql::selectQuery('
							SELECT id, `'.$this->name.'` 
							FROM '.$object->getDBTable().' as t 
							WHERE `'.$this->relationProperty.'` = '.$parentId.' 
									AND `'.$this->name.'` <= (
										SELECT `'.$this->name.'` 
										FROM '.$object->getDBTable().' 
										WHERE id = '.$id.' 
										LIMIT 1)
							ORDER BY `'.$this->name.'` DESC 
							LIMIT 2')->toArray();
						
		$up = $r[0];
		$down = $r[1];
		
		if( is_array($up) && is_array($down) ) {
			
			if( $item = call_user_func(array(get_class($object),'load'), $up['id'] ) )
				$item->{$this->name}->setPos($down[$this->name]);
			
			if( $item = call_user_func(array(get_class($object),'load'), $down['id'] ) ) 
				$item->{$this->name}->setPos($up[$this->name]);
			
			DomSql::query('UPDATE `'.$object->getTableName().'` SET `'.$this->name.'` = NULL WHERE id = '.$up['id'].''); 
			DomSql::query('UPDATE `'.$object->getTableName().'` SET `'.$this->name.'` = '.$up[$this->name].' WHERE id = '.$down['id'].''); 
			DomSql::query('UPDATE `'.$object->getTableName().'` SET `'.$this->name.'` = '.$down[$this->name].' WHERE id = '.$up['id'].''); 
		}
	}
	
	public function toMin( DomArObject $object ) {
		
		$id = $object->id;
		
		$parentId = $object->access($this->relationProperty);
		if( is_object($parentId ) ) 
			$parentId = $parentId->id;
			
			
			
		$e = DomSql::selectOneQuery('
			SELECT `'.$this->name.'` as e
			FROM '.$object->getDbTable().'
			WHERE id = '.$id.' AND `'.$this->relationProperty.'` = '.$parentId.'
			LIMIT 1','e');
		
		$s = DomSql::selectQuery('
			SELECT id, `'.$this->name.'` as o
			FROM '.$object->getDbTable().'
			WHERE `'.$this->name.'` < '.$e.' AND `'.$this->relationProperty.'` = '.$parentId.' 
			');
		
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = `'.$this->name.'`+1 WHERE `'.$this->name.'` < '.$e.' AND `'.$this->relationProperty.'` = '.$parentId.'');
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = 1 WHERE id = '.$id.'  AND `'.$this->relationProperty.'` = '.$parentId.' LIMIT 1'); 
		
		foreach( $s as $item ) {
			$ob = call_user_func(array(get_class($object),'load'), $item['id'] );
			$ob->{$this->name}->setPos( $item['o']+1 );
		}
		$object->{$this->name}->setPos(1);
		
	}
	
	public function toMax( DomArObject $object ) {

		
		$id = $object->id;
		
		$parentId = $object->access($this->relationProperty);
		if( is_object($parentId ) ) 
			$parentId = $parentId->id;
			
			
		$e = DomSql::selectOneQuery('
			SELECT `'.$this->name.'` as e
			FROM '.$object->getDbTable().'
			WHERE id = '.$id.' AND `'.$this->relationProperty.'` = '.$parentId.'
			LIMIT 1','e');
					
		$s = DomSql::selectQuery('
			SELECT id, `'.$this->name.'` as o
			FROM '.$object->getDbTable().'
			WHERE `'.$this->name.'` > '.$e.' AND `'.$this->relationProperty.'` = '.$parentId.' 
			');	
		
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = `'.$this->name.'`-1 WHERE `'.$this->name.'` > '.$e.' AND `'.$this->relationProperty.'` = '.$parentId.'');
		
		$m = DomSql::selectOneQuery('SELECT MAX(`'.$this->name.'`) as m FROM '.$object->getDbTable().' WHERE `'.$this->relationProperty.'` = '.$parentId.' LIMIT 1','m');
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.($m+1).' WHERE id = '.$id.' LIMIT 1'); 
		
		
		foreach( $s as $item ) {
			$ob = call_user_func(array(get_class($object),'load'), $item['id'] );
			$ob->{$this->name}->setPos( $item['o']-1 );
		}
		$object->{$this->name}->setPos($m+1);
	}
	
	public function swap(  DomArObject $object, $id2 ) {

		$id = $object->id;	
		if( is_object($id2) ) $id2 = $id2->id;
		
		
		$r = DomSql::selectQuery('SELECT id, `'.$this->name.'` as o FROM '.$object->getDbTable().' as t WHERE (id = '.$id.' or id = '.$id2.') LIMIT 2')->toArray();
		
		$a = $r[0];
		$b = $r[1];
		
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.$a['o'].' WHERE id = '.$b['id'].' LIMIT 1'); 
		DomSql::query('UPDATE '.$object->getDbTable().' SET `'.$this->name.'` = '.$b['o'].' WHERE id = '.$a['id'].' LIMIT 1');
		
		
		$ob = call_user_func(array(get_class($object),'load'), $a['id'] );
		$ob->{$this->name}->setPos( $b['o'] );
		
		$ob = call_user_func(array(get_class($object),'load'), $b['id'] );
		$ob->{$this->name}->setPos( $a['o'] );
			
	}
	
	public function toPos(  DomArObject $object, $pos ) {
		throw new RuntimeException('Not yet implemented');
	}
	
	public function next( DomArObject $object ) {
		/*
		if( is_object($id)) $id = $id->id;
		
		for( $i = 1; $i < func_num_args() ; $i+=2 ) $settings[func_get_arg($i)] = func_get_arg($i+1);
		
		if( isset($settings['orderBy']) )  $settings['orderBy'] = ' ORDER BY '.$settings['orderBy']; else $settings['orderBy'] = 'ORDER BY t.listOrder';	
		if( isset($settings['where']) )  $settings['where'] = ' AND '.$settings['where'];
		if( !isset($settings['limit']) ) $settings['limit'] = 1;

		$next = DomSql::selectQuery('SELECT * FROM `'.$this->getTableName().'` as t 
								JOIN `'.DomDB::tableName.'` as t2 ON (t.id = t2.id)
								WHERE t.parentId = '.$this->parent->id.' AND t.listOrder > (SELECT listOrder FROM `'.$this->getTableName().'` WHERE id = '.(int)$id.' LIMIT 1) '.$settings['where'].$settings['orderBy'].' LIMIT '.$settings['limit']);
		if( $settings['limit'] == 1 )  {
			$next = DomSql::getNext($next);
			return DomDB::factory( $next, $next['className'] );
		} else {
			$list = array();
			while( $i = DomSql::getNext($next) )
				$list[] = DomDB::factory( $i, $i['className']);
			return $list;
		}
		*/
		throw new RuntimeException('Not yet implemented');
	}
	
	public function prev( DomArObject $object ) {
		/*
		if( is_object($id)) $id = $id->id;
		
		for( $i = 1; $i < func_num_args() ; $i+=2 ) $settings[func_get_arg($i)] = func_get_arg($i+1);
		

		if( isset($settings['orderBy']) )  $settings['orderBy'] = ' ORDER BY '.$settings['orderBy']; else $settings['orderBy'] = 'ORDER BY t.listOrder DESC';	
		if( isset($settings['where']) )  $settings['where'] = ' AND '.$settings['where'];
		if( !isset($settings['limit']) ) $settings['limit'] = 1;

		$next = DomSql::selectQuery('SELECT * FROM `'.$this->getTableName().'` as t 
								JOIN `'.DomDB::tableName.'` as t2 ON (t.id = t2.id)
								WHERE t.parentId = '.$this->parent->id.' AND t.listOrder < (SELECT listOrder FROM `'.$this->getTableName().'` WHERE id = '.$id.' LIMIT 1) '. $settings['where'].$settings['orderBy'].' LIMIT '.$settings['limit']);
		
		if( $settings['limit'] == 1 ) { 
			$next = DomSql::getNext($next);
			return DomDB::factory( $next, $next['className'] );
		} else {
			$list = array();
			while( $i = DomSql::getNext($next) )
				$list[] = DomDB::factory( $i, $i['className']);
			return $list;
		}
		*/
		throw new RuntimeException('Not yet implemented');
	}
}
?>